using System;
using System.Collections.Generic;

namespace PropertyExpression.ReactiveRelation
{
    public class FuncCache
    {
        internal FuncCache()
        {
            //no-op
        }

        private readonly Dictionary<object, Dictionary<object, object>> cacheByArguments =
            new Dictionary<object, Dictionary<object, object>>();

        private readonly Dictionary<object, object> cache = new Dictionary<object, object>();

        internal TResult GetValue<T, TResult>(
            object funcKey,
            T cachedFuncArgument,
            Func<T, TResult> func)
            where T : ICachedFuncArgument
        {
            Dictionary<object, object> cuurentDictionary = null;
            object perviousArgument = null;
            foreach (var argument in cachedFuncArgument.Arguments)
            {
                if (perviousArgument == null)
                {
                    Dictionary<object, object> value;
                    if (!cacheByArguments.TryGetValue(funcKey, out value))
                    {
                        value = new Dictionary<object, object>();
                        cacheByArguments.Add(funcKey, value);
                    }
                    cuurentDictionary = value;
                }
                else
                {
                    object value;
                    if (!cuurentDictionary.TryGetValue(argument, out value))
                    {
                        value = new Dictionary<object, object>();
                        cuurentDictionary.Add(argument, value);
                    }
                    cuurentDictionary = (Dictionary<object, object>)value;
                }
                perviousArgument = argument;
            }
            if (perviousArgument == null)
            {
                object value;
                if (!cache.TryGetValue(funcKey, out value))
                {
                    value = func(cachedFuncArgument);
                    cache.Add(funcKey, value);
                }
                return (TResult)value;
            }
            else
            {
                object value;
                if (!cuurentDictionary.TryGetValue(perviousArgument, out value))
                {
                    value = func(cachedFuncArgument);
                    cuurentDictionary.Add(perviousArgument, value);
                }
                return (TResult)value;
            }
        }
    }
}